/**
 * 
 */
package com.primestone.cim.executeenddevicecontrols;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.ws.Holder;

import ch.iec.tc57._2011.enddevicecontrols.EndDeviceControl;
import ch.iec.tc57._2011.enddevicecontrols.EndDeviceControls;
import ch.iec.tc57._2011.enddevicecontrols.FaultMessage;
import ch.iec.tc57._2011.enddevicecontrolsmessage.EndDeviceControlsFaultMessageType;
import ch.iec.tc57._2011.enddevicecontrolsmessage.EndDeviceControlsPayloadType;
import ch.iec.tc57._2011.schema.message.HeaderType;
import ch.iec.tc57._2011.schema.message.MessageProperty;
import ch.iec.tc57._2011.schema.message.ReplyType;
import ch.iec.tc57._2011.schema.message.UserType;

import com.primestone.cim.util.CollectionUtil;
import com.primestone.cim.util.DateUtil;
import com.primestone.cim.util.RandomUtil;
import com.primestone.cim.util.StringUtil;
import com.primestone.cim.util.enums.CIMNoun;
import com.primestone.cim.util.enums.CIMVerb;
import com.primestone.cim.util.enums.MessageEnum;
import com.primestone.cim.util.enums.ReadingTypeEnum.TimeAttribute;
import com.primestone.cim.util.enums.RelayActionEnum;
import com.primestone.cim.util.enums.ResultEnum;

/**
 * @author cesar.mateus
 *
 */
public class Completer {

//	public static MeterReadSchedules fillMeterReadSchedules(List<ControlOperationResult> queryResult) {
//
//		MeterReadSchedules meterReadSchedules = new MeterReadSchedules();
//
//		//List<ControlOperationResult> savedMeterReadSchedules = filterSavedMeterReadSchedules(queryResult);
//		
//		
//		//fillCustomerAccountList(meterReadSchedules.getCustomerAccount());
//		//fillCustomerAgreementList(meterReadSchedules.getCustomerAgreement());
//		fillEndDeviceAssetList(meterReadSchedules.getEndDeviceAsset(), queryResult);
//		//fillEndDeviceGroupList(meterReadSchedules.getEndDeviceGroup());
//		//fillReadingTypeList(meterReadSchedules.getReadingType());
//		fillTimeScheduleList(meterReadSchedules.getTimeSchedule(), queryResult);
//
//		return meterReadSchedules;
//	}
//
//	public static void fillCustomerAccountList(List<CustomerAccount> customerAccountList) {
//		// TODO iterar sobre una lista de CustomerAccounts
//		customerAccountList.add(fillCustomerAccount());
//	}
//
//	public static CustomerAccount fillCustomerAccount(/*aca falta ver que param se necesita*/) {
//		CustomerAccount customerAccount = new CustomerAccount();
//		customerAccount.setMRID(_NULL_STRING_); // required
//		return customerAccount;
//	}
//
//	public static void fillCustomerAgreementList(List<CustomerAgreement> customerAgreementList) {
//		// TODO iterar sobre una lista de CustomerAgreements
//		customerAgreementList.add(fillCustomerAgreement());
//	}
//
//	public static CustomerAgreement fillCustomerAgreement(/*aca falta ver que param se necesita*/) {
//		CustomerAgreement customerAgreement = new CustomerAgreement();
//		customerAgreement.setMRID(_NULL_STRING_); // required
//		return customerAgreement;
//	}
//
//	public static void fillEndDeviceAssetList(List<EndDeviceAsset> endDeviceAssetList, List<ControlOperationResult> queryResult) {
//		// TODO iterar sobre una lista de EndDeviceAsset
//		//for (ControlOperationResult scheduleOperationResult : queryResult) {
//			endDeviceAssetList.add(fillEndDeviceAsset());	
//		//}
//	}
//
//	public static EndDeviceAsset fillEndDeviceAsset(/*aca falta ver que param se necesita*/) {
//		EndDeviceAsset endDeviceAsset = new EndDeviceAsset();
//		endDeviceAsset.setMRID(_NULL_STRING_); // required
//		return endDeviceAsset;
//	}
//
//	public static void fillEndDeviceGroupList(List<EndDeviceGroup> endDeviceGroupList) {
//		// TODO iterar sobre una lista de EndDeviceGroup
//		endDeviceGroupList.add(fillEndDeviceGroup());
//	}
//
//	public static EndDeviceGroup fillEndDeviceGroup(/*aca falta ver que param se necesita*/) {
//		EndDeviceGroup endDeviceGroup = new EndDeviceGroup();
//		endDeviceGroup.setGroupAddress(_NULL_BIGINTEGER_); // required
//		return endDeviceGroup;
//	}
//
//	public static void fillReadingTypeList(List<ReadingType> readingTypeList) {
//		// TODO iterar sobre una lista de ReadingType
//		readingTypeList.add(fillReadingType());
//	}
//
//	public static ReadingType fillReadingType(/*aca falta ver que param se necesita*/) {
//		ReadingType readingType = new ReadingType();
//		readingType.setMRID(_NULL_STRING_); // required
//		readingType.setName(_NULL_STRING_);
//		return readingType;
//	}
//
//	public static void fillTimeScheduleList(List<TimeSchedule> timeScheduleList, List<ControlOperationResult> queryResult) {
//		// TODO iterar sobre una lista de TimeSchedule
//		timeScheduleList.add(fillTimeSchedule());
//	}
//
//	public static TimeSchedule fillTimeSchedule(/*aca falta ver que param se necesita*/) {
//		TimeSchedule timeSchedule = new TimeSchedule();
//
//		timeSchedule.setDisabled(_NULL_BOOLEAN_);
//		timeSchedule.setOffset(_NULL_FLOAT_);
//		timeSchedule.setRecurrencePattern(_NULL_STRING_);
//		timeSchedule.setRecurrencePeriod(_NULL_FLOAT_);
//		timeSchedule.setScheduleInterval(fillScheduleInterval()); // required
//
//		fillTimePointsList(timeSchedule.getTimePoints());
//
//		return timeSchedule;
//	}
//
//	public static void fillTimePointsList(List<TimePoints> timePointsList) {
//		// TODO iterar sobre una lista de TimePoints
//		timePointsList.add(fillTimePoints());
//	}
//
//	public static TimePoints fillTimePoints(/*aca falta ver que param se necesita*/) {
//		TimePoints timePoints = new TimePoints();
//
//		timePoints.setAbsolutTime(_NULL_DATE_);
//		timePoints.setRelativeTimeinterval(_NULL_FLOAT_);
//		timePoints.setSequenceNumber(_NULL_BIGINTEGER_);
//		timePoints.setWindow(fillWindow());
//
//		return timePoints;
//	}
//
//	public static TimeSchedule.ScheduleInterval fillScheduleInterval(/*aca falta ver que param se necesita*/) {
//		TimeSchedule.ScheduleInterval scheduleInterval = new TimeSchedule.ScheduleInterval();
//		scheduleInterval.setEnd(_NULL_DATE_); // required
//		scheduleInterval.setStart(_NULL_DATE_); // required
//		return scheduleInterval;
//	}
//
//	public static TimeSchedule.TimePoints.Window fillWindow(/*aca falta ver que param se necesita*/) {
//		TimeSchedule.TimePoints.Window window = new TimeSchedule.TimePoints.Window();
//		window.setEnd(_NULL_DATE_); // required
//		window.setStart(_NULL_DATE_); // required
//		return window;
//	}

	public static String extractTicket(Holder<HeaderType> header) {
		if (header.value != null) {
			List<MessageProperty> properties = header.value.getProperty();
			for (MessageProperty messageProperty : properties) {
				if(messageProperty.getName().equals(StringUtil.TICKET_FIELD_NAME)) {
					return messageProperty.getValue();
				}
			}
		}
		return null;
	}

	public static void putTicket(Holder<HeaderType> header, String ticket) {
		if (header.value != null) {			
			MessageProperty messageProperty = new MessageProperty();
			messageProperty.setName(StringUtil.TICKET_FIELD_NAME);
			messageProperty.setValue(ticket);
			header.value.getProperty().add(messageProperty);			
		}
	}

	public static FaultMessage composeFault(String message) {
		EndDeviceControlsFaultMessageType endDeviceControlsFaultMessageType = new EndDeviceControlsFaultMessageType();
		ReplyType replyType = new ReplyType();
		replyType.setResult(ResultEnum.FAILED.getName());
		endDeviceControlsFaultMessageType.setReply(replyType);			
		return new FaultMessage(message, endDeviceControlsFaultMessageType);
	}

	/**
	 * TODO ajustar esta validacion de header de ser necesario.
	 * @param header
	 * @return
	 */
	public static FaultMessage validateHeader(Holder<HeaderType> header, CIMVerb verb) {
		FaultMessage faultMessage = null;
		if (header == null || header.value == null || header.value.getUser() == null
				|| header.value.getUser().getUserID() == null) {
			faultMessage = Completer.composeFault(MessageEnum.INCOMPLETE_USER_INFO.getDescription());			
		} else if (header.value.getVerb() == null) {			
			faultMessage = Completer.composeFault(MessageEnum.INCOMPLETE_VERB_INFO.getDescription());
		} else if (!header.value.getVerb().equals(verb.getVerb())) {
			faultMessage = Completer.composeFault(MessageEnum.INCORRECT_VERB_INFO.getDescription());
		} else if (header.value.getNoun() == null) {
			faultMessage = Completer.composeFault(MessageEnum.INCOMPLETE_NOUN_INFO.getDescription());
		} else if (!header.value.getNoun().equals(CIMNoun.END_DEVICE_CONTROL_NOUN.getNoun())) {
			faultMessage = Completer.composeFault(MessageEnum.INCORRECT_NOUN_INFO.getDescription());
		}
		return faultMessage;
	}

	/**
	 * 
	 * @param payload
	 * @return
	 */
	public static FaultMessage validatePayload(Holder<EndDeviceControlsPayloadType> payload) {
		FaultMessage faultMessage = null;
		if (payload == null || payload.value == null) {
			faultMessage = Completer.composeFault(MessageEnum.INCOMPLETE_PAYLOAD_INFO.getDescription());			
		} else {
			EndDeviceControls endDeviceControlContainerObject = payload.value.getEndDeviceControls();
			if (endDeviceControlContainerObject == null) {
				faultMessage = Completer.composeFault(MessageEnum.INCOMPLETE_PAYLOAD_EDC_INFO.getDescription());
			} else {
				List<EndDeviceControl> endDeviceControlList = endDeviceControlContainerObject.getEndDeviceControl();
				if (!CollectionUtil.safe(endDeviceControlList)) {
					faultMessage = Completer.composeFault(MessageEnum.INCOMPLETE_PAYLOAD_EDC_INFO.getDescription());				
				} else {
					for (EndDeviceControl endDeviceControl : endDeviceControlList) {
						// Validar que el payload contenga un codigo valido (Corte/Reconexion)
						if (!StringUtil.safe(endDeviceControl.getType())) {
							return Completer.composeFault(MessageEnum.INCOMPLETE_PAYLOAD_TYPE_INFO.getDescription());
						} else if (!endDeviceControl.getType().equals(ControlTypeComposer.getConnectCommandType().get(0)) && 
								   !endDeviceControl.getType().equals(ControlTypeComposer.getDisconnectCommandType().get(0)) ) {
							return Completer.composeFault(MessageEnum.INCORRECT_PAYLOAD_TYPE_INFO.getDescription());
						}
						
						if (endDeviceControl.getEndDeviceAsset() == null) {
							return Completer.composeFault(MessageEnum.INCOMPLETE_PAYLOAD_EDA_INFO.getDescription());
						} else if(!StringUtil.safe(endDeviceControl.getEndDeviceAsset().getMRID())) {
							return Completer.composeFault(MessageEnum.INCOMPLETE_PAYLOAD_EDA_MRID_INFO.getDescription());
						}
						
						if (endDeviceControl.getScheduledInterval() == null) {
							return Completer.composeFault(MessageEnum.INCOMPLETE_PAYLOAD_SI_INFO.getDescription());
						} else if (endDeviceControl.getScheduledInterval().getStart() == null) {
							return Completer.composeFault(MessageEnum.INCOMPLETE_PAYLOAD_SI_START_INFO.getDescription());
						}
					}
				}
			}
		}
		
		return faultMessage;
	}

	public static HeaderType constructExecEndDeviceControlsHeader(CIMVerb verb) {
		HeaderType headerType = new HeaderType();
		headerType.setVerb(verb.getVerb());
		headerType.setNoun(CIMNoun.END_DEVICE_CONTROL_NOUN.getNoun());
		headerType.setRevision(StringUtil.DEFAULT_REVISION);
		headerType.setContext(StringUtil.TESTING_CONTEXT);

		headerType.setTimestamp(DateUtil.getXMLGregorianCalendarFromGregorianCalendar());

		headerType.setSource(StringUtil.SOURCE);
		headerType.setMessageID(RandomUtil.nextString());

		UserType userType = new UserType();
		userType.setUserID("Test");
		userType.setOrganization(StringUtil.ORGANIZATION);

		headerType.setUser(userType);

		return headerType;
	}
	
	public static List<Integer> getDataFromRecurrencePattern(String recurrencePattern) {
		List<Integer> periodAndFrequency = new ArrayList<Integer>(); 
		TimeAttribute timeAttribute = TimeAttribute.getByIndex(recurrencePattern);
		if (timeAttribute != null) {
			switch (timeAttribute) {			
			case _1: {
				periodAndFrequency.add(DateUtil.FREQ_MINUTE);
				periodAndFrequency.add(10);
				break;
			}
			case _2: {
				periodAndFrequency.add(DateUtil.FREQ_MINUTE);
				periodAndFrequency.add(15);
				break;
			}
			case _3: {
				periodAndFrequency.add(DateUtil.FREQ_MINUTE);
				periodAndFrequency.add(1);
				break;
			}
			case _4: {
				periodAndFrequency.add(DateUtil.FREQ_HOUR);
				periodAndFrequency.add(24);
				break;
			}
			case _5: {
				periodAndFrequency.add(DateUtil.FREQ_MINUTE);
				periodAndFrequency.add(30);
				break;
			}
			case _6: {
				periodAndFrequency.add(DateUtil.FREQ_MINUTE);
				periodAndFrequency.add(5);
				break;
			}
			case _7: {
				periodAndFrequency.add(DateUtil.FREQ_MINUTE);
				periodAndFrequency.add(60);
				break;
			}
			case _10: {
				periodAndFrequency.add(DateUtil.FREQ_MINUTE);
				periodAndFrequency.add(2);
				break;
			}
			case _11: {
				periodAndFrequency.add(DateUtil.FREQ_DAY);
				periodAndFrequency.add(1);
				break;
			}
			case _13: {
				periodAndFrequency.add(DateUtil.FREQ_MONTH);
				periodAndFrequency.add(1);
				break;
			}
			case _14: {
				periodAndFrequency.add(DateUtil.FREQ_MINUTE);
				periodAndFrequency.add(3);
				break;
			}
			case _24: {
				periodAndFrequency.add(DateUtil.FREQ_WEEK);
				periodAndFrequency.add(1);
				break;
			}
			default:
				periodAndFrequency.add(DateUtil.FREQ_NONE);
				periodAndFrequency.add(0);
			}	
		} else {
			periodAndFrequency.add(DateUtil.FREQ_NONE);
			periodAndFrequency.add(0);
		}

		return periodAndFrequency;
	}
	
	public static List<ControlOperationResult> filterSavedEndDeviceControls( List<ControlOperationResult> allControls) {
		List<ControlOperationResult> savedSchedules = new ArrayList<ControlOperationResult>();
		for (ControlOperationResult controlOperationResult : allControls) {
			if ( controlOperationResult.getIsSaved() ) {
				savedSchedules.add(controlOperationResult);
			}
		}
		return savedSchedules;
	}
	
	public static ArrayList<ControlOperationResult> filterFailedEndDeviceControls( List<ControlOperationResult> allControls) {
		ArrayList<ControlOperationResult> failedSchedules = new ArrayList<ControlOperationResult>();
		for (ControlOperationResult controlOperationResult : allControls) {
			if ( !controlOperationResult.getIsSaved() ) {
				failedSchedules.add(controlOperationResult);
			}
		}
		return failedSchedules;
	}
	
	private static void populateMap(Map<String, List<ControlOperationResult>> map, String mridDevice, ControlOperationResult control) {
		List<ControlOperationResult> schedules;
		Set<String> keySet = map.keySet();
		if (keySet.contains(mridDevice)) {
			schedules = map.get(mridDevice);
			schedules.add(control);
		} else {
			schedules = new ArrayList<ControlOperationResult>();
			schedules.add(control);
		}
		map.put(mridDevice, schedules);
	}
	
	public static void classifyByDevice(Map<String, ArrayList<ControlOperationResult>> map, List<ControlOperationResult> controlResult) {
		ArrayList<ControlOperationResult> controls;
		Set<String> keySet = map.keySet();
		for (ControlOperationResult control : controlResult) {
			if (keySet.contains(control.getIdDevice())) {
				controls = map.get(control.getIdDevice());
				controls.add(control);
			} else {
				controls = new ArrayList<ControlOperationResult>();
				controls.add(control);
			}
			map.put(control.getIdDevice(), controls);		
		}	
	}

}
